package util

import (
	"reflect"
)

// 在切片的头部插入一个或多个元素
func SlicePrepend[T any](s []T, elements ...T) []T {
	// 计算新切片的长度
	newLength := len(s) + len(elements)
	// 创建一个新的切片来容纳所有元素
	newSlice := make([]T, newLength)
	// 将新元素复制到新切片的开头
	copy(newSlice, elements)
	// 将旧切片中的元素复制到新切片中，从新元素的后面开始
	copy(newSlice[len(elements):], s)
	// 返回新切片
	return newSlice
}

// 切片去重
func SliceUnique[T comparable](s []T) []T {
	// 创建一个 map 来跟踪已经遇到的元素
	seen := make(map[T]struct{})
	// 创建一个新的切片来存储不重复的元素
	var res []T
	for _, v := range s {
		// 如果数字还没有被遇到过，则添加到结果切片中
		if _, exists := seen[v]; !exists {
			seen[v] = struct{}{}
			res = append(res, v)
		}
	}
	return res
}

// 将一个大int切片分割成多个小int切片，每个小切片最多包含n个元素
func IntSliceChunk(s []int, n int) [][]int {
	var result [][]int
	for len(s) > 0 {
		// 计算当前小切片的长度，不超过n且不超过slice剩余长度
		partLen := n
		if partLen > len(s) {
			partLen = len(s)
		}
		// 从slice中截取前partLen个元素，并添加到结果中
		result = append(result, s[:partLen])
		// 更新slice为剩余部分
		s = s[partLen:]
	}
	return result
}

// 将一个大string切片分割成多个小string切片，每个小切片最多包含n个元素
func StringSliceChunk(s []string, n int) [][]string {
	var result [][]string
	for len(s) > 0 {
		// 计算当前小切片的长度，不超过n且不超过slice剩余长度
		partLen := n
		if partLen > len(s) {
			partLen = len(s)
		}
		// 从slice中截取前partLen个元素，并添加到结果中
		result = append(result, s[:partLen])
		// 更新slice为剩余部分
		s = s[partLen:]
	}
	return result
}

// 从传入的切片中随机选择n个元素（不重复），并返回一个新的切片
// 由于Go的泛型在编译时进行类型推断，因此这里不需要显式指定切片元素的类型
func RandomSlice[T any](s []T, n int) []T {
	// 如果原切片长度小于或等于n，直接返回原切片
	if len(s) <= n {
		return s
	}

	// 创建一个map来跟踪已经选择的索引，避免重复
	selectedIdx := make(map[int]struct{})
	var result []T

	// 循环直到选择了n个不重复的元素
	for len(result) < n {
		// 生成一个随机索引
		index := RandomInt(0, len(s)-1)

		// 如果这个索引还没有被选择过，就添加到结果中
		if _, exists := selectedIdx[index]; !exists {
			selectedIdx[index] = struct{}{}
			result = append(result, s[index])
		}
	}

	return result
}

// 切片交集
func SliceIntersect[T comparable](s ...[]T) []T {
	res := []T{}
	sLen := len(s)
	if sLen <= 0 {
		return res
	}
	// 统计每个元素的出现次数
	countMap := map[T]int{}
	for _, items := range s {
		for _, v := range items {
			countMap[v]++
		}
	}
	// 保留第一个切片的元素顺序为最终交集元素顺序
	// countMap 中出现次数等于切片数量的元素即为交集元素
	resMap := map[T]bool{}
	for _, v := range s[0] {
		count, countExists := countMap[v]
		_, resExists := resMap[v]
		if !countExists || count != sLen || resExists {
			continue
		}
		res = append(res, v)
	}

	return res
}

// 切片差集
// s[0] 为基准，s[0]元素再后续切片中不存在则为差集
func SliceDifference[T comparable](s ...[]T) []T {
	res := []T{}
	sLen := len(s)
	if sLen <= 0 {
		return res
	}
	compareMap := map[T]bool{}
	for i, v := range s {
		if i == 0 {
			for _, v := range v {
				compareMap[v] = true
			}
		} else {
			for _, v := range v {
				_, exists := compareMap[v]
				if exists {
					delete(compareMap, v)
				}
			}
		}
	}
	for k := range compareMap {
		res = append(res, k)
	}
	return res
}

// 判断切片元素是否都在指定切片内
// ruleS 规则切片
// s 切片
func SliceIn[T comparable](ruleS []T, s []T) bool {
	if len(ruleS) == 0 {
		return false
	}
	if len(s) == 0 {
		return true // 空切片视为全包含
	}

	// 构建ruleS的元素集合（去重优化）
	set := make(map[T]bool, len(ruleS))
	for _, v := range ruleS {
		set[v] = true
	}

	// 检查A的所有元素
	for _, v := range s {
		if !set[v] {
			return false
		}
	}
	return true
}

// 提取map切片中指定列的值，返回组成的新切片
func MapSliceColumn[T any](s []map[string]T, k string) []T {
	result := make([]T, 0, len(s))
	for _, m := range s {
		if v, exists := m[k]; exists {
			result = append(result, v)
		}
	}
	return result
}

// 提取struct切片中指定字段的值，返回组成的新切片
func StructSliceColumn[ST any, FT any](s []ST, fieldName string) []FT {
	var result []FT
	firstValid := true
	var sampleType reflect.Type

	for _, item := range s {
		var itemValue reflect.Value
		if reflect.TypeOf(item).Kind() == reflect.Ptr {
			// 如果是指针类型，先检查是否为nil
			itemValue = reflect.ValueOf(item)
			if itemValue.IsNil() { // 使用IsNil判断指针是否为nil
				continue
			}
			itemValue = itemValue.Elem() // 获取指针指向的值
		} else {
			// 非指针类型直接获取值
			itemValue = reflect.ValueOf(item)
		}
		field := itemValue.FieldByName(fieldName)
		if field.IsValid() {
			if firstValid {
				sampleType = field.Type()
				firstValid = false
			}
			if field.Type() == sampleType {
				result = append(result, field.Interface().(FT))
			}
		}
	}
	return result
}

// 过滤切片，返回满足条件的元素
func SliceFilter[T comparable](s []T, fun func(T) bool) []T {
	var result []T
	for _, item := range s {
		if fun(item) {
			result = append(result, item)
		}
	}
	return result
}

// 判断切片索引是否存在
func SliceIndexExists[T any](slice []T, index int) bool {
	return index >= 0 && index < len(slice)
}
